#ifndef EXPERIMENTALMODEL_H_JM_20130221
#define EXPERIMENTALMODEL_H_JM_20130221

#include "../generators/ModelGenerator.h"
#include "../generators/Slices.h"
#include "../boxes/DefaultGroupDefinitions.h"
#include "../boxes/nonsharedHash/HashDBOperation.h"
#include "../boxes/nonsharedHash/PreprocessDataGroupsBox.h"

#include "DataInputModelPartGenerator.h"
#include "DataOutputModelPartGenerator.h"

namespace jm4bbx {

	class  ExperimentalModel  : public ModelGenerator {
	public:
		ExperimentalModel(
			const int N, // parallelism level
			const std::string& inputBoxName,
			const InputBoxConfiguration& firstInpBoxConf,
			const InputBoxConfiguration& secondInpBoxConf,
			const std::string& actionBoxName,
			const std::string& outputBoxName,
			const std::string& outputForm,
			const std::string& typeName,
			const bool bagVersion) {
				/// Input
				DataInputModelPartGenerator inGen;
				InputBoxConfiguration confs[2] = {firstInpBoxConf, secondInpBoxConf};
				const int M = N== 1 ? N : N/2;
		
				inGen.addBoxes(this, inputBoxName, confs, 2, M, false, true);

				BoxDescriptorPtr preprocessBoxes_R = addBox("BUCKET_BOX", 
					genBoxParam(PreprocessDataGroupsBox<int>::DATA_GROUP_COUNT_PARAM_NAME, N) + 
					(N == 1 ? genBoxParam("signal_in", 1) : ""),
					M);

				BoxDescriptorPtr preprocessBoxes_S = addBox("BUCKET_BOX", 
					genBoxParam(PreprocessDataGroupsBox<int>::DATA_GROUP_COUNT_PARAM_NAME, N) + 
					(N == 1 ? genBoxParam("signal_out", 1) : ""),
					M);

				BoxDescriptorPtr actions = addBox(actionBoxName, 
					genBoxParam("signal_out", 1) +  	
					//genBoxParam("time_report", 1)	+
					genBoxParam(HashDBOperationBase::INPUT_COUNT_PARAM_NAME, M) +
					genBoxParam(DbOperationVersion::DB_OPERATION_VERSION_PARAM_NAME, bagVersion), 
					N);

				/// Output
				DataOutputModelPartGenerator oGen;
				oGen.addBoxes(this, outputBoxName, outputForm, N);
				//--------------------------------------------------------------
				//--------------------------------------------------------------
				//-------------------------------------------------------------- 

				inGen.addSlices(this, typeName);

				// vstup do bucket boxov
				addSlice(new ForkSlice(inGen.getForkDataBox(0)->getBoxId(), 
					preprocessBoxes_R->getBoxIds(), M, D_O, D_I, genTypes(typeName, 1)));

				addSlice(new ForkSlice(inGen.getForkDataBox(1)->getBoxId(), 
					preprocessBoxes_S->getBoxIds(), M, D_O, D_I, genTypes(typeName, 1)));

				// obmedzenie paralelizacie pre seriovu verziu
				if (N == 1) {
					addSlice(new PlainSlice(preprocessBoxes_S->getBoxIds()[0], preprocessBoxes_R->getBoxIds()[0], S_O, S_I));
				}

				// bucket boxy do action boxov

				auto bIds = preprocessBoxes_R->getBoxIds(); 
				for (size_t i = 0; i < bIds.size(); ++i) {
					addSlice(new CrossSlice(bIds[i], 
						actions->getBoxIds(), N, 1, D_O, D_I + i, genTypes(typeName, 1), false, true));
				}
				bIds = preprocessBoxes_S->getBoxIds(); 
				for (size_t i = 0; i < bIds.size(); ++i) {
					addSlice(new CrossSlice(bIds[i], 
						actions->getBoxIds(), N, 1, D_O, D_I + M + i, genTypes(typeName, 1), false, true));
				}

				// action boxy do fork boxu pre bucket boxy dodavajucich data v druhom kroku


				//addSlice(new MergeSlice( actions->getBoxIds(), inGen.getForkDataBox(1)->getBoxId(), 
				//	N,  S_O+1, S_I));


				//  action boxy do output boxov

				addSlice(new ParallelSlice(actions->getBoxIds(), 
					oGen.getOutputBoxes()->getBoxIds(), N, D_O, D_I, genTypes("BinaryOperationResultIndex", 1)));	

				// action do timeru

				addSlice(new MergeSlice(actions->getBoxIds(), 
					oGen.getTimerReportBox()->getBoxId(), N, S_O, S_I));

				// koniec vypoctu modelu
				oGen.addSlices(this);
		}

	};

} // namespace jm4bbx


#endif
